﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace BusLib
{
    public partial class Trip
    {
        public TimeSpan StartTime { get { return Stop_Times.First().departure_time; } }
        public TimeSpan EndTime { get { return Stop_Times.Last().arrival_time; } }
        public double Length { get { return Shape.Length; } }

        public static Trip GetById(int tripId)
        {
            return BusLibDataContext.db.Trips.Single(s => s.trip_id == tripId);
        }

        public LatLong[] GetPartialShape(Stop startStop, Stop endStop)
        {
            List<LatLong> pts = new List<LatLong>();

            pts.Add(startStop.Position);
            Stop_Time tripStartStop = Stop_Times.First(s => s.Stop == startStop);
            Stop_Time tripEndStop = Stop_Times.First(s => s.Stop == endStop);

            foreach (Shape_Point p in Shape.Shape_Points.SkipWhile(s => s != tripStartStop.Last_Shape_Point).TakeWhile(s => s != tripEndStop.Last_Shape_Point))
            {
                if (p == tripStartStop.Last_Shape_Point)
                    continue;
                pts.Add(p.Position);
            }
            pts.Add(endStop.Position);

            return pts.ToArray();
        }

        // Gets the Stop_Time for this Trip at this stop.
        public Stop_Time GetStopTime(Stop s)
        {
            return Stop_Times.First(st => st.Stop == s);
        }

        static int importCount = 0;
        static public Trip ImportCSV(string line)
        {
            Trip trip = null;
            string[] fields = line.Split(',');

            if (fields[0] != "route_id")
            {
                trip = new Trip();

                trip.route_id = int.Parse(fields[0]);
                trip.service_id = fields[1];
                trip.trip_id = int.Parse(fields[2]);
                trip.trip_headsign = fields[3];
                trip.trip_short_name = fields[4];
                trip.direction_id = int.Parse(fields[5]);
                if (fields[6] != "")
                {
                    trip.block_id = int.Parse(fields[6]);
                }
                trip.shape_id = int.Parse(fields[7]);

                BusLibDataContext.db.Trips.InsertOnSubmit(trip);

                if (++importCount > 1000)
                {
                    BusLibDataContext.db.SubmitChanges();
                    importCount = 0;
                }
            }
            return trip;
        }

        public void ComputeStopDistances()
        {
            int shapePointIdx = 0;

            foreach (Stop_Time st in Stop_Times)
            {
                double alongTrackDist = -1;
                while (shapePointIdx < Shape.Shape_Points.Count - 1)
                {
                    const double maxDistToStop = 0.02; // miles
                    // is our target anywhere near this point?
                    if (shapePointIdx != Shape.Shape_Points.Count - 2 &&
                        !GeoUtils.IsNearLine(Shape.Shape_Points[shapePointIdx].Position,
                        Shape.Shape_Points[shapePointIdx + 1].Position,
                        st.Stop.Position, maxDistToStop))
                    {
                        shapePointIdx++;
                        continue;
                    }


                    // Get the along-track distance for the current point
                    double[] dists = GeoUtils.CrossAndAlongTrackDistance(
                        Shape.Shape_Points[shapePointIdx].shape_pt_lat,
                        Shape.Shape_Points[shapePointIdx].shape_pt_lon,
                        Shape.Shape_Points[shapePointIdx+1].shape_pt_lat,
                        Shape.Shape_Points[shapePointIdx+1].shape_pt_lon,
                        st.Stop.stop_lat,
                        st.Stop.stop_lon
                        );
                    double crossTrackDist = dists[0];
                    alongTrackDist = dists[1];
                    Debug.Assert(alongTrackDist >= 0);

                    if (shapePointIdx != Shape.Shape_Points.Count - 2)
                    {
                        // If the point isn't close to our line, at all
                        if (crossTrackDist > maxDistToStop)
                        {
                            shapePointIdx++;
                            continue;
                        }

                        // look to see if we've traveled far enough to put us into the next segment
                        if (Shape.Shape_Points[shapePointIdx].shape_dist_travelled + alongTrackDist > Shape.Shape_Points[shapePointIdx + 1].shape_dist_travelled)
                        {
                            // Advance the current start-point, so we don't consider points behind us on the route
                            shapePointIdx++;
                            continue;
                        }
                    }

                    break;
                }
                st.Last_Shape_Point = Shape.Shape_Points[shapePointIdx];
                st.shape_dist_travelled = Shape.Shape_Points[shapePointIdx].shape_dist_travelled + alongTrackDist;
            }
            BusLibDataContext.db.SubmitChanges();
        }
    }
}
